﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace Client
{
    /// <summary>
    /// The main DirectX class - singleton pattern so there can only be one instance.
    /// </summary>
    public sealed class DirectX : IDisposable
    {
        public static readonly DirectX Me = new DirectX();


        public Device DirectXDevice { get { return d3ddevice; } }
        public Sprite GameSprite { get; private set; }
        public int WindowPositionX, WindowPositionY;

        private Device d3ddevice;
        private PresentParameters Parameters;
        private DisplayMode DisplayMode;

        private int dwLastTime;
        private int dwTime;
        private int dwDelay;

        /// <summary>
        /// Creates a new DirectX device and related surface, textures, etc and prepares
        /// them ready for use.
        /// </summary>
        /// <param name="renderControl">The control which will be rendered onto.</param>
        public void InitializeDirectX(Control renderControl)
        {
            Manager.DisableD3DSpy();

            try
            {
                // Get the systems default graphics card & capabilities.
                int DeviceAdapter = Manager.Adapters.Default.Adapter;
                Caps DeviceManager = Manager.GetDeviceCaps(DeviceAdapter, DeviceType.Hardware);
                CreateFlags DeviceFlags;

                // Check to see if the graphics card can handle hardware transformations & lighting.
                // If the graphics card can used mixed processing.
                // If graphics card can't handle transformations & lighting just use hardware processing.
                if (DeviceManager.DeviceCaps.SupportsHardwareTransformAndLight)
                    DeviceFlags = CreateFlags.MixedVertexProcessing;
                else
                    DeviceFlags = CreateFlags.HardwareVertexProcessing;

                // TODO - Check physical memory for processing. Use software processing if the system doesn't have enough memory.
                // TODO - Check if the graphics card can use pure hardware support.
                // TODO - Check if we can use multithreaded device flags.

                DisplayMode.Width = ClientGlobals.ScreenWidth;
                DisplayMode.Height = ClientGlobals.ScreenHeight;
                DisplayMode.Format = Format.A8R8G8B8;

                Parameters = new PresentParameters();
                Parameters.BackBufferCount = 1;
                Parameters.BackBufferFormat = DisplayMode.Format;
                Parameters.BackBufferWidth = DisplayMode.Width;
                Parameters.BackBufferHeight = DisplayMode.Height;

                Parameters.SwapEffect = SwapEffect.Discard;
                Parameters.PresentationInterval = PresentInterval.Immediate;
                Parameters.Windowed = true;

                d3ddevice = new Device(DeviceAdapter, DeviceType.Hardware, renderControl, DeviceFlags, Parameters); // Put everything into the device

                //DirectX_Device.RenderState.CullMode = Cull.Clockwise;
                //DirectX_Device.RenderState.DebugMonitorTokenEnabled = false;

                //DirectX_Device.RenderState.FillMode = FillMode.Solid;

                //DirectX_Device.RenderState.AntiAliasedLineEnable = true;
                //DirectX_Device.RenderState.MultiSampleAntiAlias = true;

                //DirectX_Device.RenderState.ShadeMode = ShadeMode.Gouraud;
                //DirectX_Device.SamplerState[0].MinFilter = TextureFilter.Anisotropic;
                //DirectX_Device.SamplerState[0].MagFilter = TextureFilter.Anisotropic;
                //DirectX_Device.SamplerState[0].MipFilter = TextureFilter.Anisotropic;
                //DirectX_Device.SamplerState[0].MaxAnisotropy = DeviceManager.MaxAnisotropy;
                //DirectX_Device.SamplerState[1].MinFilter = TextureFilter.Anisotropic;
                //DirectX_Device.SamplerState[1].MagFilter = TextureFilter.Anisotropic;
                //DirectX_Device.SamplerState[1].MipFilter = TextureFilter.Anisotropic;
                //DirectX_Device.SamplerState[1].MaxAnisotropy = DeviceManager.MaxAnisotropy;

                GameSprite = new Sprite(d3ddevice);

                d3ddevice.RenderState.Lighting = true;
                
                dwLastTime = dwTime = dwDelay = 0;
            }
            catch (DirectXException ex)
            {
                MessageBox.Show("Error Initializing DirectX. Error: " + ex.Message, "DirectX Initializing Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Rendering is done here
        /// </summary>
        public void Render()
        {
            if (d3ddevice == null) // If the device is empty don't bother rendering
            {
                return;
            }

            dwTime = Environment.TickCount;
			dwDelay = dwTime - dwLastTime;
			dwLastTime = dwTime;

            //if (dwDelay != 0)
            //{
                // We want to initally draw on the main surface and clear it each pass.
                //d3ddevice.SetRenderTarget(0, MainGameSurface);
                d3ddevice.Clear(ClearFlags.Target, Color.LightBlue, 1.0f, 0);



                d3ddevice.BeginScene();
                SceneManager.Me.RunScene(GameSprite);       // Draw scene
                WinManager.Me.DirectPaint(GameSprite);      // Draw dialogs
                GameSprite.Begin(SpriteFlags.AlphaBlend);
                DrawScreen.Me.Render(dwDelay);                     // Draw hints + system messages
                GameSprite.End();

                // draw debug
                GameSprite.Begin(SpriteFlags.AlphaBlend);
                DirectText.Me.DrawText("FPS: " + Convert.ToString(SceneManager.Me.CurrentFPS), 5, 5, Color.White);
                DirectText.Me.DrawText("Coordinates: " + Convert.ToString(MainDlg.posX) + ':' + Convert.ToString(MainDlg.posY), 5, 15, Color.White);
                GameSprite.End();
                d3ddevice.EndScene();

                d3ddevice.Present();
            //}
        }

        


        #region "IDisposable Support"
        private bool disposedValue;      // To detect redundant calls

        private void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: dispose managed state (managed objects).
                    // Free other state (managed objects).
                    if (GameSprite != null)
                    {
                        GameSprite.Dispose();
                        GameSprite = null;
                    }

                    if (d3ddevice != null)
                    {
                        d3ddevice.Dispose();
                        d3ddevice = null;
                    }
                }

                // TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
                // TODO: set large fields to null.
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}
